package com.atguigu.tingshu.common.threadpool;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Configuration
public class ThreadPoolAutoConfiguration {



    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${server.port}")
    private Integer port;

    @Autowired
    private ThreadPoolProperties threadPoolProperties;

    /**
     * 自定义一个线程池对象
     * <p>
     * <p>
     * 自定义线程池的参数个数：最少是5个。
     * int corePoolSize,线程池中的核心线程数
     * int maximumPoolSize,  线程池中的最大线程数
     * long keepAliveTime,   没有任务之后其它线程的等待时间
     * TimeUnit unit, 没有任务之后其它线程的时间单位
     * BlockingQueue<Runnable> workQueue：阻塞队列（不是普通队列）
     * <p>
     * 两个实现类用的比较多
     * ArrayBlockQueue:
     * 底层数据结构： 数组组成的数据结构
     * ArrayBlockQueue：有界阻塞队列
     * ArrayBlockQueue:底层锁的粒度很大。基于整个数组
     * ArrayBlockQueue：性能低一些，并发能力弱一些。
     * LinkedBlockQueue:
     * 底层数据结构：链表组成的数据结构
     * LinkedBlockQueue：无界阻塞队列，大小是INTEGER.NAX.VARLUE();
     * LinkedBlockQueue:底层锁的粒度相对ArrayBlockQueue小一些。基于操作的位置
     * LinkedBlockQueue:性能高一些，并发能力强一些。
     * LinkedBlockQueue：注意细节：当任务比较多情况下，拒绝策略没有作用，反而可能导致oom出现。所以用的时候一定要给一个大小
     * <p>
     * 自定义线程池的参数个数：最少是七个参数：
     * int corePoolSize, 线程池中的核心线程数
     * int maximumPoolSize, 线程池中的最大线程数
     * long keepAliveTime, 没有任务之后其它线程的等待时间
     * TimeUnit unit, 没有任务之后其它线程的时间单位
     * BlockingQueue<Runnable> workQueue, 阻塞队列（不是普通队列）
     * ThreadFactory threadFactory, 线程工厂：创建线程
     * RejectedExecutionHandler handler ：拒绝策略
     * 拒绝策略有四种：
     * 1）AbortPolicy:特点：任务直接丢弃，并且会抛出异常
     * 2）DiscardPolicy：特点： 任务直接丢弃，不会抛出异常
     * 3）DiscardOldestPolicy:特点：将任务队列中队首的任务丢掉，然后在将任务入队。（只要任务入队 任务就一定能被池中线程执行 ）
     * 4）CallRunnerPolicy:特点：会将任务回退给创建任务的线程。
     *
     * 线程池的工作原理：
     */

    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {

        // 定义一个自定义线程池对象
        /**
         * 核心线程数设置为多少一般都是根据机器的线程数来绝对
         * 任务类型：cpu密集任务还是io密集型任务
         * cpu密集型任务：一般都是cpu核心+1；  while...true.... 从百万数中找素数...
         * io密集型任务：2*cpu核数。 查询数据库 读磁盘 写数据 从网络读数据...
         * 最终一定要压测
         */

//        int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
//        int maxPoolSize = corePoolSize;  // 一般最大线程数和核心线程数保证一样

        /**
         * 自定义线程工厂
         */
        ThreadFactory threadFactory = new ThreadFactory() {

            AtomicInteger atomicInteger = new AtomicInteger(0);

            @Override
            public Thread newThread(@NotNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("【" + applicationName + "】-" + port + "-" + atomicInteger.incrementAndGet());
                // thread.start();不能在写
                return thread;
            }
        };

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threadPoolProperties.getCorePoolSize(),
                threadPoolProperties.getMaxPoolSize(),
                60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(threadPoolProperties.getQueueSize()),
                threadFactory,
                new MyRejectedExecutionHandler()
        );
        return threadPoolExecutor;
    }

    /**
     * 自定义拒绝策略
     */
    class MyRejectedExecutionHandler implements RejectedExecutionHandler {
        Logger logger = LoggerFactory.getLogger(this.getClass());

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            logger.warn("任务准备进入到自定义拒绝策略中...");
            if (!executor.isShutdown()) { // 线程池是否关闭
                r.run(); // 回退任务
            }
        }
    }


}
